package com.ruoyi.aitdemo;

import cn.hutool.crypto.digest.DigestUtil;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.ruoyi.aitcommon.anotation.ServiceLog;
import com.ruoyi.aitcommon.service.IAitEntityInfoService;
import com.ruoyi.aitcommon.utils.EntityCache;
import com.ruoyi.aitcommon.utils.ExceptionUtils;
import com.ruoyi.aitcommon.utils.JSONResult;
import com.ruoyi.aitcommon.utils.RestServiceUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import springfox.documentation.spring.web.json.Json;

import java.util.*;

/**
 * ait service示例项目
 * 调用目标系统Rest接口
 */
@Service
public class DemoRestService {
    @Autowired
    IAitEntityInfoService infoService;
    @Autowired
    EntityCache entityCache;

    /**
     * 调用外部系统服务
     * @param url 调用地址
     * @param param 接口参数
     * @param data 数据
     * @return 返回json
     */
    private JSONObject restInvoke(String url, Map<String,Object> param, Map<String,Object> data)
            throws Exception {
        ExceptionUtils.checkBlankOrUndefined(data,"没有data数据");
        /**
         * 这里构造签名或token
         * 放入header或query params中
         */
        Map<String, Object> header=null;
        Map<String, Object> params=null;
        header = this.getSignMap(param);
        params = this.getTokenMap(param);

//      按照body格式构造json
        final JSONObject bodyJson = this.getBodyJson(data,param);

//		远程调用
        final String retStr = RestServiceUtils.post(url,params, header, bodyJson.toString());

//		处理服务返回
        JSONObject retJson = JSONObject.parseObject(retStr);
        JSONObject retJson1=checkRetJson(retJson);
        return retJson1;
    }

    /**
     * 解析rest返回值,并重构返回值
     * @param json
     * @return
     */
    private JSONObject checkRetJson(JSONObject json) throws Exception {
        if (!json.containsKey("code"))
            throw new Exception("返回数据错误,无[code]!");
        final Integer code = json.getInteger("code");
        final String message = json.getString("message");
        if(code!=200){
            String msg=String.format("返回报错,code:[%s],msg[%s]",code,message);
            throw new Exception(msg);
        }
        return json;
    }

    /**
     * 构造head head中包含token或签名sign
     * 参考项目: ZDMesRestService
     * @param param 接口参数
     * @return http head
     */
    private Map<String,Object> getTokenMap(Map<String, Object> param) throws Exception {
        final Object url=param.get("url");
        ExceptionUtils.checkBlankOrUndefined(url,"url不能为空");
        JSONObject body=new JSONObject(param);
//		远程调用
        final JSONObject json = RestServiceUtils.post(url.toString(),null, body);

        if(json.getInteger("code")!=200)
            ExceptionUtils.throwException(null,"获取token失败,{}",json.getString("msg"));
        Map<String,Object> retmap=new LinkedHashMap<>();
        retmap.put("Authorization",json.getString("data"));
        return retmap;
    }

    /**
     * 构造head head中包含token或签名sign
     * 参考项目:KLWPRPRestService,QJKJWMSRestService,DDXRestService
     * @param param 接口参数(接口参数(accessKey,loginUser))
     * @return http head
     */
    private Map<String,Object> getSignMap(Map<String, Object> param) throws Exception {
        final Object accessKey = param.get("accessKey");
        final Object loginUser = param.get("loginUser");
        ExceptionUtils.checkBlankOrUndefined(accessKey,"accessKey不能为空");
        ExceptionUtils.checkBlankOrUndefined(loginUser,"loginUser");

        final long timestamp = new Date().getTime();
        final String plainText = loginUser.toString() + accessKey.toString() + timestamp;
        final String sign= DigestUtil.md5Hex(plainText).toUpperCase();

        Map<String,Object> retmap=new LinkedHashMap<>();
//        retmap.put("accessKey",accessKey);//系统内置,不用传递
        retmap.put("sign",sign);
        retmap.put("loginUser",loginUser);
        retmap.put("timestamp",timestamp);
        return retmap;
    }

    /**
     * 将数据按照接口Body格式进行封装
     * @param data 数据
     * @return body str
     */
    private JSONObject getBodyJson(Map<String,Object> data,Map<String,Object> context){
        JSONObject json=new JSONObject(data);
        return json;
    }

    /**
     * 查询接口封装
     * @param url        调用地址
     * @param context     接口参数
     * @param query        查询条件JSON
     * @return 查询结果数据
     */
    @ServiceLog("查询服务")
    public List<Map<String, Object>> query(String url, Map<String, Object> context, Map<String, Object> query) throws Exception {
        List<Map<String,Object>> retlist=new ArrayList<Map<String,Object>>();
//        获取sign
//        final Map<String, Object> header = this.getSignHeader(context);
//        final Map<String, Object> header = this.getTokenHeader(context);

//        构造query json
        JSONObject queryJSON=new JSONObject(query);

//		远程调用
        final JSONObject retjson = RestServiceUtils.post(url, null, queryJSON);

//		处理返回值
        if(retjson.getInteger("code")!=200)
            ExceptionUtils.throwException(null,"查询报错,错误消息:{}",retjson.getString("msg"));
        final JSONArray retdatas = retjson.getJSONArray("data");
        for(Object data:retdatas) {
            retlist.add((Map<String,Object>)data);
        }
        return retlist;
    }

    /**
     * 常用接口,保存接口封装
     * @param datas 数据集合,如果存在单头单体组合,需要在flow中组合后调用该方法
     * @param param 接口参数或上下文
     * @param url 外部接口地址
     * @param entityInfo 实体信息
     */
    @ServiceLog("保存服务")
    public List<JSONObject> entitySave(List<Map<String, Object>> datas,Map<String, Object> param
            ,String url, JSONObject entityInfo) throws Exception {
        List<JSONObject> retlist = new ArrayList<>();
        ExceptionUtils.checkBlankOrUndefined(url, "url为空");
        ExceptionUtils.checkBlankOrUndefined(entityInfo, "entityInfo为空");
        ExceptionUtils.checkBlankOrUndefined(param,"param 为空");

//		过滤缓存
        final List<Map<String, Object>> filterList = entityCache.filterEntityChange(entityInfo, datas);

        for (Map<String,Object> data : filterList) {
//			循环单条处理
            try {
//				调用接口
                JSONObject retjson = this.restInvoke(url,param, data);
//				处理实体日志和缓存
                infoService.insertAitEntityInfo(entityInfo, data, null);
//				处理返回
                retlist.add(retjson);
            } catch (Exception ex) {
//				处理实体日志和缓存-失败信息
                infoService.insertAitEntityInfo(entityInfo, data, ex);
//				处理返回
                retlist.add(JSONResult.error(ex.getMessage()));
            }
        }
        return retlist;
    }

}
